<?php

    namespace thebuggenie\modules\agile\entities;

    use thebuggenie\core\entities\common\IdentifiableScoped;
    use thebuggenie\core\framework;

    /**
     * Agile board class
     *
     * @author Daniel Andre Eikeland <zegenie@zegeniestudios.net>
     * @version 3.1
     * @license http://opensource.org/licenses/MPL-2.0 Mozilla Public License 2.0 (MPL 2.0)
     * @package thebuggenie
     * @subpackage agile
     */

    /**
     * Agile board class
     *
     * @package thebuggenie
     * @subpackage agile
     *
     * @Table(name="\thebuggenie\modules\agile\entities\tables\AgileBoards")
     */
    class AgileBoard extends IdentifiableScoped
    {

        const TYPE_GENERIC = 0;
        const TYPE_SCRUM = 1;
        const TYPE_KANBAN = 2;

        const SWIMLANES_ISSUES = 'issues';
        const SWIMLANES_GROUPING = 'grouping';
        const SWIMLANES_EXPEDITE = 'expedite';

        /**
         * The name of the board
         *
         * @var string
         * @Column(type="string", length=200)
         */
        protected $_name;

        /**
         * Board description
         *
         * @var string
         * @Column(type="string", length=200)
         */
        protected $_description;

        /**
         * Whether this board is the private
         *
         * @var boolean
         * @Column(type="boolean", default=1)
         */
        protected $_is_private = true;

        /**
         * @var \thebuggenie\core\entities\User
         * @Column(type="integer", length=10)
         * @Relates(class="\thebuggenie\core\entities\User")
         */
        protected $_user_id;

        /**
         * @var \thebuggenie\core\entities\Project
         * @Column(type="integer", length=10)
         * @Relates(class="\thebuggenie\core\entities\Project")
         */
        protected $_project_id;

        /**
         * @var \thebuggenie\core\entities\Issuetype
         * @Column(type="integer", length=10)
         * @Relates(class="\thebuggenie\core\entities\Issuetype")
         */
        protected $_epic_issuetype_id;

        /**
         * @var \thebuggenie\core\entities\Issuetype
         * @Column(type="integer", length=10)
         * @Relates(class="\thebuggenie\core\entities\Issuetype")
         */
        protected $_task_issuetype_id;

        /**
         * @var \thebuggenie\core\entities\SavedSearch
         * @Column(type="integer", length=10)
         * @Relates(class="\thebuggenie\core\entities\SavedSearch")
         */
        protected $_backlog_search_id;

        /**
         * @var integer
         * @Column(type="integer", length=10)
         */
        protected $_autogenerated_search;

        /**
         * The board type
         *
         * @var integer
         * @Column(type="integer", length=10)
         */
        protected $_type = self::TYPE_SCRUM;

        /**
         * Whether to use swimlanes
         *
         * @var boolean
         * @Column(type="boolean", default=false)
         */
        protected $_use_swimlanes = false;

        protected $_swimlanes = array();

        /**
         * Swimlane type
         *
         * @var string
         * @Column(type="string", length=50, default="issuetype")
         */
        protected $_swimlane_type = self::SWIMLANES_ISSUES;

        /**
         * Swimlane identifier field
         *
         * @var string
         * @Column(type="string", length=50, default="issuetype")
         */
        protected $_swimlane_identifier = "issuetype";

        /**
         * Swimlane field value
         *
         * @var array
         * @Column(type="serializable", length=500)
         */
        protected $_swimlane_field_values = array();

        /**
         * Cached search object
         * @var \thebuggenie\core\entities\SavedSearch
         */
        protected $_search_object;

        /**
         * Array of epic issues
         *
         * @var array|\thebuggenie\core\entities\Issue
         */
        protected $_epic_issues = null;

        /**
         * Board columns
         *
         * @var array|\thebuggenie\modules\agile\entities\BoardColumn
         * @Relates(class="\thebuggenie\modules\agile\entities\BoardColumn", collection=true, foreign_column="board_id", orderby="sort_order")
         */
        protected $_board_columns = null;

        /**
         * Issue field value
         *
         * @var array
         * @Column(type="serializable", length=500)
         */
        protected $_issue_field_values = array();

        /**
         * Returns the associated user
         *
         * @return \thebuggenie\core\entities\User
         */
        public function getUser()
        {
            return $this->_b2dbLazyLoad('_user_id');
        }

        public function setUser($user)
        {
            $this->_user_id = $user;
        }

        /**
         * Returns the associated project
         *
         * @return \thebuggenie\core\entities\Project
         */
        public function getProject()
        {
            return $this->_b2dbLazyLoad('_project_id');
        }

        public function setProject($project)
        {
            $this->_project_id = $project;
        }

        /**
         * Returns the associated epic issue type
         *
         * @return \thebuggenie\core\entities\Issuetype
         */
        public function getEpicIssuetype()
        {
            return $this->_b2dbLazyLoad('_epic_issuetype_id');
        }

        public function getEpicIssuetypeID()
        {
            return ($this->getEpicIssuetype() instanceof \thebuggenie\core\entities\Issuetype) ? $this->getEpicIssuetype()->getID() : 0;
        }

        public function setEpicIssuetype($epic_issuetype_id)
        {
            $this->_epic_issuetype_id = $epic_issuetype_id;
        }

        /**
         * Returns the associated task issue type
         *
         * @return \thebuggenie\core\entities\Issuetype
         */
        public function getTaskIssuetype()
        {
            return $this->_b2dbLazyLoad('_task_issuetype_id');
        }

        public function setTaskIssuetype($task_issuetype_id)
        {
            $this->_task_issuetype_id = $task_issuetype_id;
        }

        public function getTaskIssuetypeID()
        {
            return ($this->getTaskIssuetype() instanceof \thebuggenie\core\entities\Issuetype) ? $this->getTaskIssuetype()->getID() : 0;
        }

        /**
         * Returns the associated backlog saved search
         *
         * @return \thebuggenie\core\entities\SavedSearch
         */
        public function getBacklogSearch()
        {
            return $this->_b2dbLazyLoad('_backlog_search_id');
        }

        public function setBacklogSearch($backlog_search)
        {
            $this->_backlog_search_id = $backlog_search;
            $this->_autogenerated_search = null;
            $this->_search_object = null;
        }

        public function setAutogeneratedSearch($autogenerated_search)
        {
            $this->_autogenerated_search = $autogenerated_search;
            $this->_backlog_search_id = null;
            $this->_search_object = null;
        }

        public function getAutogeneratedSearch()
        {
            return $this->_autogenerated_search;
        }

        public function usesAutogeneratedSearchBacklog()
        {
            return (bool) $this->_autogenerated_search;
        }

        public function usesSavedSearchBacklog()
        {
            return (bool) $this->_backlog_search_id;
        }

        /**
         * Returns the associated search object
         *
         * @return \thebuggenie\core\entities\SavedSearch
         */
        public function getBacklogSearchObject()
        {
            if ($this->_search_object === null)
            {
                if ($this->usesSavedSearchBacklog())
                {
                    $this->_search_object = $this->getBacklogSearch();
                }
                elseif (!$this->_search_object instanceof \thebuggenie\core\entities\SavedSearch)
                {
                    $this->_search_object = \thebuggenie\core\entities\SavedSearch::getPredefinedSearchObject($this->_autogenerated_search);
                    $this->_search_object->setFilter('issuetype', \thebuggenie\core\entities\SearchFilter::createFilter('issuetype', array('o' => '!=', 'v' => $this->getEpicIssuetypeID())));
                    $this->_search_object->setFilter('milestone', \thebuggenie\core\entities\SearchFilter::createFilter('milestone', array('o' => '!=', 'v' => null)));
                }
                $this->_search_object->setIssuesPerPage(0);
                $this->_search_object->setOffset(0);
                $this->_search_object->setSortFields(array(\thebuggenie\core\entities\tables\Issues::MILESTONE_ORDER => 'desc'));
                $this->_search_object->setGroupBy(null);
            }

            return $this->_search_object;
        }

        public function getBacklogSearchIdentifier()
        {
            return ($this->usesAutogeneratedSearchBacklog()) ? 'predefined_' . $this->getAutogeneratedSearch() : 'saved_' . $this->getBacklogSearchObject()->getID();
        }

        public function getName()
        {
            return $this->_name;
        }

        public function setName($name)
        {
            $this->_name = $name;
        }

        public function getDescription()
        {
            return $this->_description;
        }

        public function hasDescription()
        {
            return (bool) ($this->getDescription() != '');
        }

        public function setDescription($description)
        {
            $this->_description = $description;
        }

        public function getIsPrivate()
        {
            return $this->_is_private;
        }

        public function isPrivate()
        {
            return $this->getIsPrivate();
        }

        public function setIsPrivate($is_private)
        {
            $this->_is_private = $is_private;
        }

        public function getType()
        {
            return $this->_type;
        }

        public function setType($type)
        {
            $this->_type = $type;
        }

        public function getBacklogIssuesUrl()
        {
            if ($this->usesSavedSearchBacklog())
            {
                $url = \framework\Context::getRouting()->generate('project_issues', array('project_key' => $this->getProject()->getKey(), 'saved_search' => $this->getBacklogSearch()->getID(), 'search' => true, 'format' => 'backlog'));
            }
            else
            {
                $url = \framework\Context::getRouting()->generate('project_issues', array('project_key' => $this->getProject()->getKey(), 'predefined_search' => $this->getAutogeneratedSearch(), 'search' => true, 'format' => 'backlog'));
            }

            return $url;
        }

        public function getEpicIssues()
        {
            if ($this->_epic_issues === null)
            {
                $this->_epic_issues = \thebuggenie\core\entities\tables\Issues::getTable()->getOpenIssuesByProjectIDAndIssuetypeID($this->getProject()->getID(), $this->getEpicIssuetypeID());
            }
            return $this->_epic_issues;
        }

        public function getMilestones()
        {
            return $this->getProject()->getOpenMilestones();
        }

        public function getDefaultSelectedMilestone()
        {
            foreach ($this->getMilestones() as $milestone)
            {
                if (!$milestone->isReached())
                {
                    return $milestone;
                }
            }
        }

        public function getReleases()
        {
            return $this->getProject()->getUnreleasedBuilds();
        }

        public function usesSwimlanes()
        {
            return $this->_use_swimlanes;
        }

        public function getSwimlaneType()
        {
            return $this->_swimlane_type;
        }

        public function getSwimlaneIdentifier()
        {
            return $this->_swimlane_identifier;
        }

        public function getSwimlaneFieldValues()
        {
            return $this->_swimlane_field_values;
        }

        public function getIssueFieldValues()
        {
            return $this->_issue_field_values;
        }

        public function setUseSwimlanes($use_swimlanes = true)
        {
            $this->_use_swimlanes = $use_swimlanes;
        }

        public function useSwimlanes($use_swimlanes = true)
        {
            $this->setUseSwimlanes($use_swimlanes);
        }

        public function setSwimlaneType($swimlane_type)
        {
            $this->_swimlane_type = $swimlane_type;
        }

        public function clearSwimlaneType()
        {
            $this->_swimlane_type = null;
        }

        public function setSwimlaneIdentifier($swimlane_identifier)
        {
            $this->_swimlane_identifier = $swimlane_identifier;
        }

        public function clearSwimlaneIdentifier()
        {
            $this->_swimlane_identifier = null;
        }

        public function setSwimlaneFieldValues($swimlane_field_values)
        {
            $this->_swimlane_field_values = $swimlane_field_values;
        }

        public function clearSwimlaneFieldValues()
        {
            $this->_swimlane_field_values = array();
        }

        public function setIssueFieldValues($issue_field_values)
        {
            $this->_issue_field_values = $issue_field_values;
        }

        public function clearIssueFieldValues()
        {
            $this->_issue_field_values = array();
        }

        public function hasSwimlaneFieldValue($value)
        {
            return in_array($value, $this->getSwimlaneFieldValues());
        }

        public function hasSwimlaneFieldValues()
        {
            return (count($this->getSwimlaneFieldValues()) > 0);
        }

        public function hasIssueFieldValue($value)
        {
            return in_array($value, $this->getIssueFieldValues());
        }

        public function hasIssueFieldValues()
        {
            return (count($this->getIssueFieldValues()) > 0);
        }

        /**
         * Returns an array of board columns
         *
         * @return array|\thebuggenie\modules\agile\entities\BoardColumn
         */
        public function getColumns()
        {
            return $this->_b2dbLazyLoad('_board_columns');
        }

        protected function _populateMilestoneSwimlanes(\thebuggenie\core\entities\Milestone $milestone)
        {
            if (!array_key_exists($milestone->getID(), $this->_swimlanes))
            {
                $this->_swimlanes[$milestone->getID()] = array();
                $swimlanes = array();
                if ($this->usesSwimlanes())
                {
                    switch ($this->getSwimlaneType())
                    {
                        case self::SWIMLANES_EXPEDITE:
                        case self::SWIMLANES_GROUPING:
                            switch ($this->getSwimlaneIdentifier())
                            {
                                case 'priority':
                                    $items = \thebuggenie\core\entities\Priority::getAll();
                                    break;
                                case 'severity':
                                    $items = \thebuggenie\core\entities\Severity::getAll();
                                    break;
                                case 'category':
                                    $items = \thebuggenie\core\entities\Category::getAll();
                                    break;
                                default:
                                    $items = array();
                                    break;
                            }
                            if ($this->getSwimlaneType() == self::SWIMLANES_EXPEDITE)
                            {
                                $expedite_items = array();
                                foreach ($this->getSwimlaneFieldValues() as $value)
                                {
                                    if (array_key_exists($value, $items))
                                    {
                                        $expedite_items[$items[$value]->getID()] = $items[$value];
                                        unset($items[$value]);
                                    }
                                }

                                $swimlanes[] = array('identifiables' => $expedite_items);
                                $swimlanes[] = array('identifiables' => $items);
                                $swimlanes[] = array('identifiables' => 0);
                            }
                            else
                            {
                                foreach ($items as $item)
                                {
                                    $swimlanes[] = array('identifiables' => $item);
                                }
                                $swimlanes[] = array('identifiables' => 0);
                            }
                            break;
                        case self::SWIMLANES_ISSUES:
                            foreach ($milestone->getIssues() as $issue)
                            {
                                if ($issue->isChildIssue())
                                {
                                    foreach ($issue->getParentIssues() as $parent)
                                    {
                                        if ($parent->getIssueType()->getID() != $this->getEpicIssuetypeID()) continue 2;
                                    }
                                }

                                if (in_array($issue->getIssueType()->getID(), $this->getSwimlaneFieldValues()))
                                {
                                    $swimlanes[] = array('identifiables' => $issue);
                                }
                            }
                            $swimlanes[] = array('identifiables' => 0);
                            break;
                    }
                }
                else
                {
                    $swimlanes[] = array('identifiables' => 0);
                }

                foreach ($swimlanes as $details)
                {
                    $swimlane = new BoardSwimlane();
                    $swimlane->setBoard($this);
                    $swimlane->setIdentifiables($details['identifiables']);
                    $swimlane->setMilestone($milestone);
                    $this->_swimlanes[$milestone->getID()][] = $swimlane;
                }
            }
        }

        /**
         * Retrieve all available swimlanes for the selected milestone
         *
         * @param \thebuggenie\core\entities\Milestone $milestone
         * @return array|BoardSwimlane
         */
        public function getMilestoneSwimlanes($milestone)
        {
            if (!($milestone instanceof \thebuggenie\core\entities\Milestone))
            {
                return array();
            }

            $this->_populateMilestoneSwimlanes($milestone);

            return $this->_swimlanes[$milestone->getID()];
        }

    }
